﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using MetaCollection.Framework.Change;
using MetaCollection.Framework.Model;
using System.Globalization;

namespace MetaCollection.Framework.Repository
{
	internal class DistributedResourceRepository : DistributedRepository<IResourceRepository>, IResourceRepository
	{

		#region IResourceRepository Members

		/// <summary>
		/// Gets the resource stream for the given item.
		/// </summary>
		/// <remarks>Attempts to resolve a reference to which repo will have 
		/// the resource.  If there is no reference then it tries them all.  If we
		/// find one then we mark it for next time and return.
		/// 
		/// Note: this method assumes that there can only be one instance of the
		/// resource among the resource providers.  If there are more than one then
		/// the first provider that knows of the resource will be used.</remarks>
		/// <param name="item">The item to get the resource stream for</param>
		/// <returns>
		/// A stream to read the item's resource from
		/// </returns>
		public Stream GetResourceStream(IItemInstance item)
		{
			if (item == null)
				throw new ArgumentNullException("item", "Item can not be null.");

			Stream result = Stream.Null;
			string key = ReferenceKey(item.ParentId, null);
			foreach (var repo in ResolveReference(key))
			{
				result = repo.GetResourceStream(item);
				if (result != null && result != Stream.Null)
				{
					MarkReference(key, repo); // record where we found this for quicker access next time.
					break;
				}
			}
			return result;
		}

		/// <summary>
		/// Saves the resource stream provided for the given item.
		/// </summary>
		/// <remarks>
		/// Attempts to resolve a reference to the item in a repo.  If there is no 
		/// reference then we try the parent id... if that fails too then we use
		/// the first resource provider in the system to persist the resource.
		/// </remarks>
		/// <param name="item">The item associated with this resource stream.</param>
		/// <param name="data">The data to save as the resource for this item.</param>
		public void SaveResourceStream(IItemInstance item, Stream data)
		{
			if (item == null)
				throw new ArgumentNullException("item", "Item can not be null.");
			if (data == null)
				throw new ArgumentNullException("data", "The data stream can not be null.");


			IResourceRepository repo;
			if (IsKnownReference(ReferenceKey(item.Id, null)))
				repo = ResolveReference(ReferenceKey(item.Id, null)).First();
			else if (IsKnownReference(ReferenceKey(item.ParentId, null)))
				repo = ResolveReference(ReferenceKey(item.Id, null)).First();
			else // TODO: if there are no marked references we try doing a couple gets then checking again before using the default.
				repo = DefaultRepository;
			repo.SaveResourceStream(item, data);
			MarkReference(ReferenceKey(item.Id, null), repo);
		}

		#endregion

	}
}
